using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using PropertyExpression.Common;

namespace PropertyExpression.ReactiveRelation
{
    internal static class ExpressionWrapperExtensions
    {
        internal static PropertyInfo GetReversePropertyInfo<TEntity, TProperty>(
            this IPropertyExpressionProxy<TEntity, TProperty> propertyExpressionProxy)
        {
            return propertyExpressionProxy.GetPropertyInfo().GetReversePropertyInfo();
        }

        internal static IEnumerable<object> GetReversePropertyValue<TEntity, TProperty>(
            this IPropertyExpressionProxy<TEntity, TProperty> propertyExpressionProxy,
            object sender,
            IPreviousValueProcessor previousValueProcessor)
        {
            //TODO: improve performance
            var reversePropertyInfo = propertyExpressionProxy.GetReversePropertyInfo();
            var result = reversePropertyInfo.GetValue(sender, null).ToEnumerable();
            previousValueProcessor.Process().ProcessValue(
                previousValueProvider =>
                    {
                        if (sender == previousValueProvider.Sender)
                        {
                            if (reversePropertyInfo.Name == previousValueProvider.PropertyName)
                            {
                                result = result.Concat(previousValueProvider.PreviousValue.ToEnumerable());
                            }
                            else
                            {
                                //TODO: improve performance
                                reversePropertyInfo.GetAssociationAttribute().ProcessValue(
                                    associationAttribute =>
                                        {
                                            if (associationAttribute.ThisKey.Split(InternalUtil.Separator)
                                                .Any(key => key == previousValueProvider.PropertyName))
                                            {
                                                throw new InvalidOperationException("LINQ to SQL does not support correct upgrade of an entity set when there is a change of foreign key value.");
                                            }
                                        }
                                    );
                            }
                        }
                    }
                );
            return result;
        }

        internal static string GetReversePropertyInfoName<TEntity, TProperty>(
            this IPropertyExpressionProxy<TEntity, TProperty> propertyExpressionProxy)
        {
            return propertyExpressionProxy.GetReversePropertyInfo().Name;
        }

        internal static string GetReturnValueTypeGenericArgumentFullName<TEntity, TProperty>(
            this IPropertyExpressionProxy<TEntity, TProperty> propertyExpressionProxy)
        {
            return InternalUtil.GenericArgument(typeof(TProperty)).FullName;
        }

        private static IEnumerable<object> ToEnumerable(this object value)
        {
            if (value != null)
            {
                yield return value;
            }
        }
    }
}